﻿// Inner Fire 游戏引擎库
// RootMotion - RootMotion计算器
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于保存每一帧应该用的RootMotion，混合计算等等。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-06-10

#include "root_motion.h"
#include <game/animation/animation.h>
#include <game/animation/snapshot.h>
#include <graphics/imath.h>
#include <klog.h>

using namespace DirectX;

namespace ifire::game {
RootMotion::RootMotion(Snapshot* parent)
    : parent_(parent)
    , last_(VEC0)
    , delta_(VEC0)
    , last_frame_(-1.0f)
    , root_motion_axis_(IAxis::Z) {}

void RootMotion::Execute(float frame, IMat& mat) {
  // 提取出Offset
  auto translate = imath::PickTranslate(mat);
  XMFLOAT3 pos;
  XMStoreFloat3(&pos, translate);

  // KLOG("Translation: %.3f,%.3f,%.3f", VEC_PF(translate));

  // 根据设置的轴来区别
  // 我们根据是哪个轴，来决定Translation的旋转。
  // 默认是的Z轴，不进行旋转。如果是Y轴，就表示Z变到了Y，其它的相应修改。
  if (root_motion_axis_ == IAxis::Z) {
    mat.r[3].m128_f32[2] = 0.0f; // 移除Z
  } else if (root_motion_axis_ == IAxis::Y) {
    mat.r[3].m128_f32[1] = 0.0f; // 移除Y
    imath::Swap(pos.z, pos.y);
    translate = XMLoadFloat3(&pos);
  } else {
    mat.r[3].m128_f32[0] = 0.0f; // 移除X
    imath::Swap(pos.z, pos.x);
    translate = XMLoadFloat3(&pos);
  }  

  // 与上一帧计算Delta
  SetDelta(frame, translate);
}

void RootMotion::Reset() {
  last_ = VEC0;
  last_frame_ = -1.0f;
  delta_ = VEC0;
}

void RootMotion::SetDelta(float frame, IVec translate) {
  // 如果last_frame是-1，那么表示被重置了。如果这时候插入的帧是突然的帧，
  // 比如，一下子就插到第10帧。这时的Delta需要进行一个推断计算，因为没有上一帧参考。
  // 处于0帧的时候不受这个影响，因为0帧直接获得translate。
  if (last_frame_ < 0.0f) {
    // 得到需要分割的项目
    float scale = 1.0f / (frame + 1.0f);
    // 将Translate做除法。(为什么是负值参考上面）
    delta_ = VEC0 - XMVectorScale(translate, scale);
  } else {
    // 两种情况，一种是正常的延续，一种是被循环了
    if (last_frame_ > frame) { // 被循环
      delta_ = translate;
    } else { // 正常
      delta_ = last_ - translate;
    }
  }

  // 保存上一次记录
  last_ = translate;
  last_frame_ = frame;
}
} // namespace ifire::game